#include "StdAfx.h"
#include "CCrypt.h"

CCryptKey::CCryptKey()
{
	m_KeyType=0;
	m_Prov=0;
	m_Key=0;
}

BOOL CCryptKey::InitProv(LPCSTR Container,DWORD KeyType)
{
	DWORD ProvType=0;
	m_KeyType=KeyType;
	m_Container=Container;
	switch(m_KeyType)
	{
	case CCRYPT_RSA:
		{
			ProvType=PROV_RSA_FULL;
		}
		break;
	case CCRYPT_DES:
		{
			ProvType=PROV_RSA_SCHANNEL;
		}
		break;
	}
	CryptAcquireContextA(&m_Prov,m_Container,0,ProvType,CRYPT_DELETEKEYSET);
	return CryptAcquireContextA(&m_Prov,m_Container,0,ProvType,CRYPT_NEWKEYSET);
}

void CCryptKey::ReleaseProv()
{
	if(m_Prov)
	{
		DWORD ProvType=0;
		switch(m_KeyType)
		{
		case CCRYPT_RSA:
			{
				ProvType=PROV_RSA_FULL;
			}
			break;
		case CCRYPT_DES:
			{
				ProvType=PROV_RSA_SCHANNEL;
			}
			break;
		}
		CryptAcquireContextA(&m_Prov,m_Container,0,ProvType,CRYPT_DELETEKEYSET);
		CryptReleaseContext(m_Prov,0);
	}
}

BOOL CCryptKey::GetKey(string& KeyBlob,DWORD WParam,DWORD LParam)
{
	DWORD dwBlobType=0;
	switch(m_KeyType)
	{
	case CCRYPT_RSA:
		{
			dwBlobType=WParam?PRIVATEKEYBLOB:PUBLICKEYBLOB;
		}
		break;
	case CCRYPT_DES:
		{
			dwBlobType=OPAQUEKEYBLOB;
		}
		break;
	}

	DWORD dwBlobSize=0;
	CryptExportKey(m_Key,0,dwBlobType,0,0,&dwBlobSize);

	BOOL ok=FALSE;
	if(dwBlobSize>0)
	{
		KeyBlob.resize(dwBlobSize);
		ok=CryptExportKey(m_Key,0,dwBlobType,0,(byte*)KeyBlob.c_str(),&dwBlobSize);;
	}

	return ok;
}

BOOL CCryptKey::ImportKey(string& KeyBlob,DWORD WParam,DWORD LParam)
{
	BOOL ok=FALSE;
	switch(m_KeyType)
	{
	case CCRYPT_RSA:
		{
			ok=CryptImportKey(m_Prov,(byte*)KeyBlob.c_str(),KeyBlob.size(),0,0,&m_Key);
		}
		break;
	case CCRYPT_DES:
		{
			ok=CryptImportKey(m_Prov,(byte*)KeyBlob.c_str(),KeyBlob.size(),0,0,&m_Key);
		}
		break;
	}
	return ok;
}

CCrypt::CCrypt(void)
{
}

CCrypt::~CCrypt(void)
{
}

CCrypt* CCrypt::GetInstance()
{
	static CCrypt ins;
	return &ins;
}

BOOL CCrypt::GenKey(CCryptKey* Key,DWORD WParam,DWORD LParam)
{
	DWORD dwFlag=0;
	switch(Key->m_KeyType)
	{
	case CCRYPT_RSA:
		{
			dwFlag|=CRYPT_EXPORTABLE;
			dwFlag|=WParam<<16;
		}
		break;
	case CCRYPT_DES:
		{
			dwFlag|=CRYPT_EXPORTABLE;
		}
		break;
	}
	return CryptGenKey(Key->m_Prov,Key->m_KeyType,dwFlag,&Key->m_Key);
}

BOOL CCrypt::SignHash(CCryptKey* Key,DWORD WParam,DWORD LParam)
{
	HCRYPTHASH hhash=0;
	string* hashData=(string*)LParam;

	BeginTry();
	switch(Key->m_KeyType)
	{
	case CCRYPT_RSA:
		{
			DWORD srclen=hashData->size();
			BASSERT(CryptCreateHash(Key->m_Prov,CALG_SHA1,0,0,&hhash));
			BASSERT(CryptHashData(hhash,(byte*)hashData->c_str(),srclen,0));
			DWORD hashLength=2048;
			hashData->resize(hashLength);
			BASSERT(CryptSignHash(hhash,AT_SIGNATURE,0,0,(byte*)hashData->c_str(),&hashLength));
			hashData->resize(hashLength);
		}
		break;
	case CCRYPT_DES:
		{		
			BASSERT(CalHash(Key,CALG_SHA1,*hashData));
			DWORD datalen=hashData->size();
			DWORD hashLength=2048;
			hashData->resize(hashLength);
			BASSERT(CryptEncrypt(Key->m_Key,0,TRUE,0,(byte*)hashData->c_str(),&datalen,hashLength));
			hashData->resize(datalen);
		}
		break;
	}
	EndTry();
	Finally();

	if(hhash)CryptDestroyHash(hhash);
	return GetResult();
}

BOOL CCrypt::VerifyData(CCryptKey* Key,DWORD WParam,DWORD LParam)
{
	HCRYPTHASH hhash=0;
	string* srcData=(string*)WParam;
	string* hashData=(string*)LParam;

	BeginTry();
	switch(Key->m_KeyType)
	{
	case CCRYPT_RSA:
		{
			DWORD srclen=srcData->size();
			BASSERT(CryptCreateHash(Key->m_Prov,CALG_SHA1,0,0,&hhash));
			BASSERT(CryptHashData(hhash,(byte*)srcData->c_str(),srclen,0));
			BASSERT(CryptVerifySignature(hhash,(byte*)hashData->c_str(),hashData->size(),Key->m_Key,0,0));
		}
		break;
	case CCRYPT_DES:
		{
			BASSERT(CalHash(Key,CALG_SHA1,*srcData));
			DWORD datalen=hashData->size();
			BASSERT(CryptDecrypt(Key->m_Key,hhash,1,0,(byte*)hashData->c_str(),&datalen));
			hashData->resize(datalen);
			BASSERT(srcData->size()==hashData->size());
			BASSERT(memcmp(srcData->c_str(),hashData->c_str(),srcData->size())==0);
		}
		break;
	}

	EndTry();
	Finally();
	if(hhash)CryptDestroyHash(hhash);
	return GetResult();
}

BOOL CCrypt::CalHash(DWORD HashType,string& HashData)
{
	CCryptKey tmpKey;
	BOOL ok=FALSE;
	if(tmpKey.InitProv("CRYPTHASHTMP",CCRYPT_RSA))
	{
		ok=CalHash(&tmpKey,HashType,HashData);
		tmpKey.ReleaseProv();
	}
	return ok;
}

BOOL CCrypt::CalHash(CCryptKey* Key,DWORD HashType,string& HashData)
{
	HCRYPTHASH hhash=0;
	BeginTry();
	BASSERT(CryptCreateHash(Key->m_Prov,HashType,0,0,&hhash));
	BASSERT(CryptHashData(hhash,(byte*)HashData.c_str(),HashData.size(),0));
	DWORD hashDataSizeBufSize=4;
	DWORD hashDataSize=0;
	BASSERT(CryptGetHashParam(hhash,HP_HASHSIZE,(BYTE*)&hashDataSize,&hashDataSizeBufSize,0));
	BASSERT(hashDataSize>0);
	HashData.resize(hashDataSize);
	BASSERT(CryptGetHashParam(hhash,HP_HASHVAL,(byte*)HashData.c_str(),&hashDataSize,0));
	EndTry();
	Finally();
	if(hhash)CryptDestroyHash(hhash);
	return GetResult();
}